package net.entelijan.config;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.List;

import net.entelijan.config.impl.BeanA;
import net.entelijan.config.impl2.PathUtil;

import org.junit.Test;

public class PathUtilTestCase {

	@Test
	public void testNull() {
		List<List<String>> pathes = PathUtil.splitToPossiblePathes(null);
		assertEquals(0, pathes.size());
	}

	@Test
	public void testEmpty() {
		List<List<String>> pathes = PathUtil.splitToPossiblePathes("");
		assertEquals(0, pathes.size());
	}

	@Test
	public void testOneItem00() {
		List<List<String>> pathes = PathUtil.splitToPossiblePathes("hallo");
		assertEquals(1, pathes.size());
		List<String> path = pathes.iterator().next();
		assertEquals(1, path.size());
		String pathElem = path.iterator().next();
		assertEquals("hallo", pathElem);
	}

	@Test
	public void testOneItem00a() {
		List<List<String>> pathes = PathUtil
				.splitToPossiblePathes("halloButton");
		assertEquals(2, pathes.size());
		assertEquals("halloButton", pathes.get(0).get(0));
		assertEquals("hallo", pathes.get(1).get(0));
		assertEquals("button", pathes.get(1).get(1));
	}

	@Test
	public void testOneItem00b() {
		List<List<String>> pathes = PathUtil
				.splitToPossiblePathes("halloTextField");
		assertEquals(4, pathes.size());
		assertEquals("halloTextField", pathes.get(0).get(0));

		assertEquals("halloText", pathes.get(1).get(0));
		assertEquals("field", pathes.get(1).get(1));

		assertEquals("hallo", pathes.get(2).get(0));
		assertEquals("textField", pathes.get(2).get(1));

		assertEquals("hallo", pathes.get(3).get(0));
		assertEquals("text", pathes.get(3).get(1));
		assertEquals("field", pathes.get(3).get(2));
	}

	@Test
	public void testOneItem01() {
		List<List<String>> pathes = PathUtil.splitToPossiblePathes("a");
		assertEquals(1, pathes.size());
		List<String> path = pathes.iterator().next();
		assertEquals(1, path.size());
		String pathElem = path.iterator().next();
		assertEquals("a", pathElem);
	}

	@Test
	public void testOneItem01a() {
		List<List<String>> pathes = PathUtil.splitToPossiblePathes("aButton");
		assertEquals(2, pathes.size());
		assertEquals("aButton", pathes.get(0).get(0));
		assertEquals("a", pathes.get(1).get(0));
		assertEquals("button", pathes.get(1).get(1));
	}

	@Test
	public void testOneItem02() {
		List<List<String>> pathes = PathUtil.splitToPossiblePathes("a1");
		assertEquals(1, pathes.size());
		List<String> path = pathes.iterator().next();
		assertEquals(1, path.size());
		String pathElem = path.iterator().next();
		assertEquals("a1", pathElem);
	}

	@Test
	public void testOneItem03() {
		List<List<String>> pathes = PathUtil
				.splitToPossiblePathes("a1092381023");
		assertEquals(1, pathes.size());
		List<String> path = pathes.iterator().next();
		assertEquals(1, path.size());
		String pathElem = path.iterator().next();
		assertEquals("a1092381023", pathElem);
	}

	@Test
	public void testFindMatchingPath00() {
		List<List<String>> pathes = cratePathes("a.b.c|ab.c|a.bc|abc");
		assertNull(PathUtil.findMatchingPath(pathes, BeanA.class));
	}

	@Test
	public void testFindMatchingPath01() {
		List<List<String>> pathes = cratePathes("beanB|a.c.d.f");
		assertEquals("beanB", createString(PathUtil.findMatchingPath(pathes,
				BeanA.class)));
	}

	@Test
	public void testFindMatchingPath02() {
		List<List<String>> pathes = cratePathes("beanA.beanB|beanB.beanC");
		assertEquals("beanB.beanC", createString(PathUtil.findMatchingPath(
				pathes, BeanA.class)));
	}

	@Test
	public void testFindMatchingPath03() {
		List<List<String>> pathes = cratePathes("beanA.beanB.valX|beanA.BeanC|beanA.beanB.valC");
		assertEquals(null, createString(PathUtil.findMatchingPath(pathes,
				BeanA.class)));
	}

	@Test
	public void testFindMatchingPath04() {
		List<List<String>> pathes = cratePathes("beanB.beanC.valX|beanB.BeanC.valX|beanA.beanB.valA");
		assertEquals("beanB.beanC.valX", createString(PathUtil
				.findMatchingPath(pathes, BeanA.class)));
	}

	@Test
	public void testFindMatchingPath041() {
		List<List<String>> pathes = cratePathes("beanB.BeanC.valX|beanA.beanB.valA|beanB.beanC.valX");
		assertEquals("beanB.beanC.valX", createString(PathUtil
				.findMatchingPath(pathes, BeanA.class)));
	}

	@Test
	public void testFindMatchingPath042() {
		List<List<String>> pathes = cratePathes("beanB.BeanC.valX|beanB.beanC.valX|beanA.beanB.valA");
		assertEquals("beanB.beanC.valX", createString(PathUtil
				.findMatchingPath(pathes, BeanA.class)));
	}

	@Test
	public void testFindMatchingPath05() {
		List<List<String>> pathes = cratePathes("beanB.beanC|beanB.d.f.g");
		assertEquals("beanB.beanC", createString(PathUtil.findMatchingPath(
				pathes, BeanA.class)));
	}

	@Test
	public void testGetHeads00() {
		List<List<String>> pathes = cratePathes("a.b.c.d.e|x.y.c.d");
		assertEquals("a.b", createString(PathUtil.getHeads(pathes, "c").get(0)));
		assertEquals("x.y", createString(PathUtil.getHeads(pathes, "c").get(1)));
	}

	@Test
	public void testGetHeads01() {
		List<List<String>> pathes = cratePathes("a.b.c.d.e|x.y.c.d");
		assertEquals("a.b.c.d.e", createString(PathUtil.getHeads(pathes, "A")
				.get(0)));
		assertEquals("x.y.c.d", createString(PathUtil.getHeads(pathes, "A")
				.get(1)));
	}

	@Test
	public void testGetHeads02() {
		List<List<String>> pathes = cratePathes("a.b.c.d.e|x.y.c.d");
		assertEquals("", createString(PathUtil.getHeads(pathes, "a").get(0)));
		assertEquals("x.y.c.d", createString(PathUtil.getHeads(pathes, "a")
				.get(1)));
	}

	@Test
	public void testHasMatchingHead00() {
		List<List<String>> pathes = cratePathes("a.b.c.d.e|x.y.c.d");
		List<List<String>> heads = cratePathes("a.b|x.y.c.d");
		assertTrue(PathUtil.hasMatchingHead(pathes, heads));
	}

	@Test
	public void testHasMatchingHead01() {
		List<List<String>> pathes = cratePathes("a.b.c.d.e|x.y.c.d");
		List<List<String>> heads = cratePathes("s.d.f|x");
		assertTrue(PathUtil.hasMatchingHead(pathes, heads));
	}

	@Test
	public void testCalculateIndexCombinations00() {
		test(0);
	}

	@Test
	public void testCalculateIndexCombinations01() {
		test(1);
	}

	@Test
	public void testCalculateIndexCombinations02() {
		test(2);
	}

	@Test
	public void testCalculateIndexCombinations03() {
		test(3);
	}

	@Test
	public void testCalculateIndexCombinations04() {
		test(4);
	}

	@Test
	public void testCalculateIndexCombinations05() {
		test(5);
	}

	private void test(int len) {
		List<List<List<Integer>>> isCombis = PathUtil
				.calculateIndexCombinations(len, 0);
		List<List<List<Integer>>> shouldCombis = calculateIndexCombinationsDefined(len);
		assertEqualsCombis(shouldCombis, isCombis);
	}

	private void assertEqualsCombis(List<List<List<Integer>>> shouldCombis,
			List<List<List<Integer>>> isCombis) {
		assertEquals(shouldCombis.size(), isCombis.size());
		for (List<List<Integer>> isCombi : isCombis) {
			if (!matchesOne(shouldCombis, isCombi)) {
				fail(isCombi + " does not match any from " + shouldCombis);
			}
		}
	}

	private boolean matchesOne(List<List<List<Integer>>> shouldCombis,
			List<List<Integer>> isCombi) {
		boolean re = false;
		for (List<List<Integer>> shouldCombi : shouldCombis) {
			if (matches(shouldCombi, isCombi)) {
				re = true;
				break;
			}
		}
		return re;
	}

	private boolean matches(List<List<Integer>> shouldCombi,
			List<List<Integer>> isCombi) {
		boolean re = true;
		for (List<Integer> is : isCombi) {
			if (!matchesOne1(shouldCombi, is)) {
				re = false;
				break;
			}
		}
		return re;
	}

	private boolean matchesOne1(List<List<Integer>> shouldCombi,
			List<Integer> is) {
		boolean re = false;
		for (List<Integer> should : shouldCombi) {
			if (matches1(should, is)) {
				re = true;
				break;
			}
		}
		return re;
	}

	private boolean matches1(List<Integer> should, List<Integer> is) {
		boolean re = true;
		if (should.size() != is.size()) {
			re = false;
		} else {
			for (Integer isInt : is) {
				if (!should.contains(isInt)) {
					re = false;
					break;
				}
			}
		}
		return re;
	}

	private String createString(List<String> findMatchingPath) {
		String re = null;
		if (findMatchingPath != null) {
			StringBuilder builder = new StringBuilder();
			int i = 0;
			for (String s : findMatchingPath) {
				if (i > 0) {
					builder.append(".");
				}
				builder.append(s);
				i++;
			}
			re = builder.toString();
		}
		return re;
	}

	private List<List<String>> cratePathes(String p) {
		List<List<String>> re = new ArrayList<List<String>>();
		String[] p1 = p.split("\\|");
		for (String p2 : p1) {
			List<String> re1 = new ArrayList<String>();
			String[] p3 = p2.split("\\.");
			for (String p4 : p3) {
				re1.add(p4);
			}
			re.add(re1);
		}
		return re;
	}

	private static List<List<List<Integer>>> calculateIndexCombinationsDefined(
			int length) {
		List<List<List<Integer>>> re;
		switch (length) {
		case 0:
			re = new ArrayList<List<List<Integer>>>();
			break;
		case 1:
			re = createIndexMatrix(new Integer[][][] { { { 0 } } });
			break;
		case 2:
			re = createIndexMatrix(new Integer[][][] { { { 0, 1 } },
					{ { 0 }, { 1 } } });
			break;
		case 3:
			re = createIndexMatrix(new Integer[][][] { { { 0, 1, 2 } },
					{ { 0 }, { 1, 2 } }, { { 0, 1 }, { 2 } },
					{ { 0 }, { 1 }, { 2 } }, });
			break;
		case 4:
			re = createIndexMatrix(new Integer[][][] { // 
			{ { 0, 1, 2, 3 } }, //
					{ { 0, 1, 2 }, { 3 } }, //
					{ { 0, 1 }, { 2, 3 } }, //
					{ { 0, 1 }, { 2 }, { 3 } }, //
					{ { 0 }, { 1, 2, 3 } }, //
					{ { 0 }, { 1, 2 }, { 3 } }, //
					{ { 0 }, { 1 }, { 2, 3 } }, //
					{ { 0 }, { 1 }, { 2 }, { 3 } } });
			break;
		case 5:
			re = createIndexMatrix(new Integer[][][] { // 
			{ { 0, 1, 2, 3, 4 } }, //
					{ { 0, 1, 2, 3 }, { 4 } }, //
					{ { 0, 1, 2 }, { 3, 4 } }, //
					{ { 0, 1, 2 }, { 3 }, { 4 } }, //
					{ { 0, 1 }, { 2, 3, 4 } }, //
					{ { 0, 1 }, { 2, 3 }, { 4 } }, //
					{ { 0, 1 }, { 2 }, { 3, 4 } }, //
					{ { 0, 1 }, { 2 }, { 3 }, { 4 } }, //
					{ { 0 }, { 1, 2, 3, 4 } }, //
					{ { 0 }, { 1, 2, 3 }, { 4 } }, //
					{ { 0 }, { 1, 2 }, { 3, 4 } }, //
					{ { 0 }, { 1, 2 }, { 3 }, { 4 } }, //
					{ { 0 }, { 1 }, { 2, 3, 4 } }, //
					{ { 0 }, { 1 }, { 2, 3 }, { 4 } },//
					{ { 0 }, { 1 }, { 2 }, { 3, 4 } },//
					{ { 0 }, { 1 }, { 2 }, { 3 }, { 4 } } //
			});
			break;
		default:
			throw new IllegalArgumentException(
					"Cannot calculate index combinations for " + length);
		}
		return re;
	}

	private static List<List<List<Integer>>> createIndexMatrix(
			Integer[][][] integers) {
		ArrayList<List<List<Integer>>> re = new ArrayList<List<List<Integer>>>();
		for (Integer[][] matrix : integers) {
			ArrayList<List<Integer>> r0 = new ArrayList<List<Integer>>();
			for (Integer[] row : matrix) {
				ArrayList<Integer> r1 = new ArrayList<Integer>();
				for (int val : row) {
					r1.add(val);
				}
				r0.add(r1);
			}
			re.add(r0);
		}
		return re;
	}

}
